﻿namespace Digitalmes.Core.Domain.Repositories;

/// <summary>
/// 仓储扩展对象。
/// </summary>
public static class IRepositoryExtensions
{
    /// <summary>
    /// 转换为可查询的对象。
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="repo"></param>
    /// <returns></returns>
    public static ISugarQueryable<T> AsQueryable<T>(this IRepository<T> repo)
        where T : class, IEntity, new()
    {
        return repo.Context.Queryable<T>();
    }

    /// <summary>
    /// 转换为可更新的对象。
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="repo"></param>
    /// <returns></returns>
    public static IUpdateable<T> AsUpdateable<T>(this IRepository<T> repo)
        where T : class, IEntity, new()
    {
        return repo.Context.Updateable<T>();
    }

    /// <summary>
    /// 转换为可删除的对象。
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="repo"></param>
    /// <returns></returns>
    public static IDeleteable<T> AsDeleteable<T>(this IRepository<T> repo)
        where T : class, IEntity, new()
    {
        return repo.Context.Deleteable<T>();
    }

    /// <summary>
    /// 分页查询。
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="repo"></param>
    /// <param name="pageNumber">页号</param>
    /// <param name="pageSize">每页数量</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public static async Task<(List<T> items, int total)> GetPagationAsync<T>(this IRepository<T> repo, Expression<Func<T, bool>>? where,
         int pageNumber, int pageSize, CancellationToken cancellationToken = default)
         where T : class, IEntity, new()
    {
        return await repo.AsQueryable()
            .WhereIF(where != null, where)
            .ToPagationAsync(pageNumber, pageSize, cancellationToken);
    }

    /// <summary>
    /// 可排序的分页查询。
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="repo"></param>
    /// <param name="pageNumber">页号</param>
    /// <param name="pageSize">每页数量</param>
    /// <param name="order">排序</param>
    /// <param name="isDesc">是否按逆序查询</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public static async Task<(List<T> items, int total)> GetPagationAsync<T>(this IRepository<T> repo, Expression<Func<T, bool>>? where,
        int pageNumber, int pageSize,
        Expression<Func<T, object>> order, bool isDesc = true, CancellationToken cancellationToken = default)
        where T : class, IEntity, new()
    {
        return await repo.AsQueryable()
            .WhereIF(where != null, where)
            .OrderBy(order, isDesc ? OrderByType.Desc : OrderByType.Asc)
            .ToPagationAsync(pageNumber, pageSize, cancellationToken);
    }

    /// <summary>
    /// 根据可排序条件查询列表。
    /// </summary>
    /// <param name="where">筛选条件</param>
    /// <param name="order">排序</param>
    /// <param name="isDesc">是否按逆序查询</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public static async Task<List<T>> GetListAsync<T>(this IRepository<T> repo, Expression<Func<T, bool>> where,
        Expression<Func<T, object>> order, bool isDesc = true, CancellationToken cancellationToken = default)
        where T : class, IEntity, new()
    {
        return await repo.AsQueryable()
            .Where(where)
            .OrderBy(order, isDesc ? OrderByType.Desc : OrderByType.Asc)
            .ToListAsync(cancellationToken);
    }

    /// <summary>
    /// 查询数据总数目。
    /// </summary>
    /// <returns></returns>
    public static async Task<int> CountAsync<T>(this IRepository<T> repo, CancellationToken cancellationToken = default)
        where T : class, IEntity, new()
    {
        return await repo.AsQueryable()
            .CountAsync(cancellationToken);
    }

    /// <summary>
    /// 根据筛选条件查询第一条满足的数据，没有找到则返回 null。
    /// </summary>
    /// <param name="where">筛选条件</param>
    /// <returns></returns>
    public static async Task<T?> FirstOrDefaultAsync<T>(this IRepository<T> repo, Expression<Func<T, bool>> where,
        CancellationToken cancellationToken = default)
        where T : class, IEntity, new()
    {
        return await repo.AsQueryable()
            .Where(where)
            .FirstAsync(cancellationToken);
    }

    /// <summary>
    /// 根据筛选条件查询第一条满足的数据，没有找到则返回 null。
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="repo"></param>
    /// <param name="where">筛选条件</param>
    /// <param name="order">排序</param>
    /// <param name="isDesc">是否按逆序查询</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public static async Task<T?> FirstOrDefaultAsync<T>(this IRepository<T> repo, Expression<Func<T, bool>> where,
        Expression<Func<T, object>> order, bool isDesc = true, CancellationToken cancellationToken = default)
        where T : class, IEntity, new()
    {
        return await repo.AsQueryable()
            .Where(where)
            .OrderBy(order, isDesc ? OrderByType.Desc : OrderByType.Asc)
            .FirstAsync(cancellationToken);
    }

    /// <summary>
    /// 根据筛选条件，按 <see cref="IEntityBase.Id"/> 排序获取最新的一条数据，没有找到则返回 null。
    /// </summary>
    /// <param name="where">筛选条件</param>
    /// <returns></returns>
    public static async Task<T?> LastOrDefaultAsync<T>(this IRepository<T> repo, Expression<Func<T, bool>> where,
        CancellationToken cancellationToken = default)
        where T : class, IEntityBase, new()
    {
        return await repo.AsQueryable()
            .Where(where)
            .OrderBy(m => new { m.Id }, OrderByType.Desc)
            .FirstAsync(cancellationToken);
    }

    /// <summary>
    /// 根据筛选条件，查找唯一的一条记录，没有找到则返回 null。
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="repo"></param>
    /// <param name="where">筛选条件</param>
    /// <returns></returns>
    public static async Task<T?> SingleOrDefaultAsync<T>(this IRepository<T> repo, Expression<Func<T, bool>> where)
        where T : class, IEntity, new()
    {
        return await repo.AsQueryable()
            .Where(where)
            .SingleAsync();
    }

    /// <summary>
    /// 插入数据，并返回主键 Id。
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="repo"></param>
    /// <param name="insertObj">要插入是数据</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public static async Task<long> InsertReturnIdentityAsync<T>(this IRepository<T> repo, T insertObj, CancellationToken cancellationToken)
        where T : class, IEntityBase, new()
    {
        return await repo.Context.Insertable(insertObj).ExecuteReturnBigIdentityAsync(cancellationToken);
    }

    /// <summary>
    /// 新增数据，若已存在则更新。
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="repo"></param>
    /// <param name="data">要新增或更新的数据</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public static async Task<bool> InsertOrUpdateAsync<T>(this IRepository<T> repo, T data, CancellationToken cancellationToken = default)
        where T : class, IEntityBase, new()
    {
        return await repo.Context.Storageable(data).ExecuteCommandAsync(cancellationToken) > 0;
    }

    /// <summary>
    /// 批量新增数据，若已存在则更新。
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="repo"></param>
    /// <param name="datas">要新增或更新的数据集合</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public static async Task<bool> InsertOrUpdateAsync<T>(this IRepository<T> repo, List<T> datas, CancellationToken cancellationToken = default)
       where T : class, IEntityBase, new()
    {
        return await repo.Context.Storageable(datas).ExecuteCommandAsync(cancellationToken) > 0;
    }

    /// <summary>
    /// 更新实体，并指定要更新的列名。
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="repo"></param>
    /// <param name="entry">实体对象</param>
    /// <param name="updateColumns">要更新的列名</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public static async Task<bool> UpdateAsync<T>(this IRepository<T> repo, T entry, Expression<Func<T, object>> updateColumns,
        CancellationToken cancellationToken = default)
        where T : class, IEntityBase, new()
    {
        return await repo.Context.Updateable(entry)
           .UpdateColumns(updateColumns)
           .ExecuteCommandAsync(cancellationToken) > 0;
    }

    /// <summary>
    /// 批量更新实体，并指定要更新的列名。
    /// </summary>
    /// <param name="entries">实体实体集合</param>
    /// <param name="updateColumns">要更新的列名</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public static async Task<bool> UpdateAsync<T>(this IRepository<T> repo, List<T> entries, Expression<Func<T, object>> updateColumns,
        CancellationToken cancellationToken = default)
        where T : class, IEntityBase, new()
    {
        return await repo.Context.Updateable(entries)
           .UpdateColumns(updateColumns)
           .ExecuteCommandAsync(cancellationToken) > 0;
    }

    /// <summary>
    /// 删除实体。
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="repo"></param>
    /// <param name="id">要删除的实体Id</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    /// <remarks>若删除对象实体实现了 <see cref="ISoftDelete"/> 接口，则会进行软删除。</remarks>
    public static async Task<bool> DeleteByIdAsync<T>(this IRepository<T> repo, long id, CancellationToken cancellationToken = default)
        where T : class, IEntityBase, new()
    {
        if (typeof(ISoftDelete).IsAssignableFrom(typeof(T)))
        {
            return await repo.AsUpdateable()
                .SetColumns(nameof(ISoftDelete.IsDeleted), true)
                .Where(m => m.Id == id)
                .ExecuteCommandAsync(cancellationToken) > 0;
        }

        return await repo.AsDeleteable().In(id).ExecuteCommandAsync(cancellationToken) > 0;
    }

    /// <summary>
    /// 删除实体。
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="repo"></param>
    /// <param name="ids">要删除的实体Id集合</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    /// <remarks>若删除对象实体实现了 <see cref="ISoftDelete"/> 接口，则会进行软删除。</remarks>
    public static async Task<bool> DeleteByIdAsync<T>(this IRepository<T> repo, long[] ids, CancellationToken cancellationToken = default)
        where T : class, IEntityBase, new()
    {
        if (typeof(ISoftDelete).IsAssignableFrom(typeof(T)))
        {
            return await repo.AsUpdateable()
                .SetColumns(nameof(ISoftDelete.IsDeleted), true)
                .Where(m => ids.Contains(m.Id))
                .ExecuteCommandAsync(cancellationToken) > 0;
        }

        return await repo.AsDeleteable().In(ids).ExecuteCommandAsync(cancellationToken) > 0;
    }

    /// <summary>
    /// 删除实体。
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="repo"></param>
    /// <param name="deleteObj">要删除的对象</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    /// <remarks>若删除对象实体实现了 <see cref="ISoftDelete"/> 接口，则会进行软删除。</remarks>
    public static async Task<bool> DeleteAsync<T>(this IRepository<T> repo, T deleteObj, CancellationToken cancellationToken = default)
        where T : class, IEntityBase, new()
    {
        if (typeof(ISoftDelete).IsAssignableFrom(typeof(T)))
        {
            return await repo.AsUpdateable()
                .SetColumns(nameof(ISoftDelete.IsDeleted), true)
                .Where(m => m.Id == deleteObj.Id)
                .ExecuteCommandAsync(cancellationToken) > 0;
        }

        return await repo.AsDeleteable().Where(deleteObj).ExecuteCommandAsync(cancellationToken) > 0;
    }

    /// <summary>
    /// 批量删除实体。
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="repo"></param>
    /// <param name="deleteObjs">要删除的对象集合</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    /// <remarks>若删除对象实体实现了 <see cref="ISoftDelete"/> 接口，则会进行软删除。</remarks>
    public static async Task<bool> DeleteAsync<T>(this IRepository<T> repo, List<T> deleteObjs, CancellationToken cancellationToken = default)
        where T : class, IEntityBase, new()
    {
        if (typeof(ISoftDelete).IsAssignableFrom(typeof(T)))
        {
            return await repo.AsUpdateable()
                .SetColumns(nameof(ISoftDelete.IsDeleted), true)
                .Where(m => deleteObjs.Any(s => s.Id == m.Id))
                .ExecuteCommandAsync(cancellationToken) > 0;
        }

       return await repo.AsDeleteable().Where(deleteObjs).ExecuteCommandAsync(cancellationToken) > 0;
    }

    /// <summary>
    /// 跟您筛选条件删除对象。
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="repo"></param>
    /// <param name="where">筛选条件</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    /// <remarks>若删除对象实体实现了 <see cref="ISoftDelete"/> 接口，则会进行软删除。</remarks>
    public static async Task<bool> DeleteAsync<T>(this IRepository<T> repo, Expression<Func<T, bool>> where, CancellationToken cancellationToken = default)
        where T : class, IEntity, new()
    {
        return typeof(ISoftDelete).IsAssignableFrom(typeof(T))
            ? await repo.AsUpdateable()
                .SetColumns(nameof(ISoftDelete.IsDeleted), true)
                .Where(where)
                .ExecuteCommandAsync(cancellationToken) > 0
            : await repo.AsDeleteable().Where(where).ExecuteCommandAsync(cancellationToken) > 0;
    }
}
